Strange behaviour of Git: mysterious changes cannot be undone
The Linux source tree has filenames which differ in case only, which causes interesting failures on systems with case-insensitive filesystems.
You need a case-sensitive filesystem in order to work with the Linux source.
(include/linux/netfilter/xt_connmark.h
and include/linux/netfilter/xt_CONNMARK.h
are two different files in the Git repository, but only one can exist in your checkout at a time if your filesystem is case-insensitive.)
Git shows file contents changed after clone
Check these questions:
- Cloning a git repo, and it already has a dirty working directory... Whaaaaa?
- Strange behaviour of Git: mysterious changes cannot be undone
This problem is caused by the OS X filesystem being case insensitive.
Cloning a git repo, and it already has a dirty working directory... Whaaaaa?
Which OS are you using? This error is caused by your filesystem not being case sensitive, like the default on Mac OS X.
How can I discard modified files?
A git reset --hard HEAD
should solve the problem.
Strange behavior from git log --since
By default, the date in git log
will show in the default format.
--date=default shows timestamps in the original timezone (either committer’s or author’s).
Based on Git help log
--date=local shows timestamps in user’s local timezone.
--date=default shows timestamps in the original timezone (either committer’s or author’s).
I suggest you run git log --date=local --since=<your date>
. It's supposed to show all commits in your local machine's time.
Now, back to your question about --since
.
If --since=<date>
is without specific a time, it will use your local time at the time you run this command.
For example,
--since "20-09-2013"
when you run this command at 09AM, will act as
--since "20-09-2013 09:00:00"
so, that's why it doesn't make some commits on that day show up. (because it already passed your current time)
if you would like to search for all commits for today then use
--since "20-09-2013 00:00:00"
back to mystery story
So, now git log --since=19-09-2013.
That returns
Date: Fri Sep 20 08:04:13 2013 +0200
Date: Fri Sep 20 08:03:28 2013 +0200
Date: Fri Sep 20 08:02:05 2013 +0200
Date: Thu Sep 19 09:53:10 2013 +0200
What I guess that you have run this command from 08:04:13 afterward on 20 Sep 2013. (Based on your hint that when you use since today, it shows nothing.).
How to force git to use fast-forward when applying a stash?
TL;DR
You need to git reset --hard HEAD
(or anything equivalent) before applying with --index
like this. All the usual caveats around hard resets apply.
Long
I linked in a comment to How do I properly git stash/pop in pre-commit hooks to get a clean working tree for tests? which shows how to do the final pop (or equivalent), and some of the caveats around this. However, the answer to the question as asked—specifically How to force git to use fast-forward when applying a stash—is that you can't, and in fact, the question doesn't even make sense: fast-forward is a different concept from stashing and unstashing.1
A Git stash is simply a set of commits (two unless you use the --all
or --include-untracked
option, then you get three) with a special arrangement. The commits save:
- the index at the time of
git stash
(usinggit write-tree
); - the work-tree contents at the time of
git stash
(using rather complex code); - and last in this list, but actually done earlier, if you did use
--all
or--include-untracked
, the files that were untracked including ignored files (--all
) or the files that were untracked excluding ignored files (--include-untracked
).
Git then resets the work-tree, normally to match the HEAD
commit, and if --all
or --include-untracked
were used, removes the files stored in the third commit as well. When you use --keep-index
, though, Git resets the work-tree to match the index contents.
The reference named refs/stash
is modified to point to the work-tree commit. This commit has, as its parents, the HEAD
commit (parent #1), the index commit (parent #2), and if present, the untracked-files commit (parent #3). The index has as its parent the HEAD
commit. The untracked-files commit has no parent (is a root commit):
...--o--o--o <-- refs/heads/somebranch (HEAD)
|\
i-w <-- refs/stash
/
u
or more typically, the same without u
.
When git stash
resets to HEAD
(i.e., without --keep-index
), all you have to do undo what git stash
did is run git stash pop --index
(note: not --keep-index
!). This runs git stash apply
with the same options and arguments,2 and if that succeeds without merge conflicts, runs git stash drop
on the same stash.
The apply can use both the index commit and the work-tree commit to recover what you were working on, but by default, it ignores the index commit. Adding --index
tells Git to apply the index commit (turned into a diff against the current index contents) to the current index contents, using git apply --index
. If this fails, git stash
stops and does nothing. In this I would suggest one turn the stash into a new branch using git stash branch
, though git stash
merely suggests applying without --index
.3
In any case, Git then tries to apply the work-tree commit to the current work-tree.4 If you had stashed without --keep-index
, and made no changes to the current work-tree, this would always succeed: the current index and work-tree would match the HEAD
commit so this would leave the current index unchanged and apply all the differences in the work-tree commit to the work-tree itself, resulting in recovery of the stashed work-tree.
The problem at this point is that you did use --keep-index
, so the current work-tree matches the index that you set up, rather than matching the HEAD
commit. Hence, before you apply the stash (with or without --index
), you must first reset the work-tree to match the HEAD
commit, i.e., git reset --hard
. The index and work-tree states you want are in the stash you're about to apply, so this is safe as long as the current index and work-tree have not been modified by whatever pre-commit / pre-push code you have.
Once you have done that, a git apply --index
of the stash commits will restore both the index and the work-tree (modulo that bug in the linked question!).
Footnotes
These are out of order on purpose because footnote 1 is so long.
2The argument to git stash apply
defaults to refs/stash
. If you give it any argument, the behavior is a little fancier: in recent versions of Git, if you give it an all-numeric argument n it examines stash@{n}
, otherwise it uses whatever you gave it. It passes this string to git rev-parse
to make sure that it converts to a valid hash ID, and that when suffixed with :
, ^1
, ^1:
, ^2
, and ^2:
, those also convert to valid hash IDs. If the string produces a valid hash ID with both ^3
and ^3:
, those are also remembered. These collectively form the w_commit
, w_tree
, b_commit
, b_tree
, i_commit
, and i_tree
, plus the u_commit
and u_tree
if they exist. See the gitrevisions documentation for how this works in more detail.
What this boils down to is that any argument you pass to git stash apply
must have the form of a merge commit, with at least two parents. Git does not check whether there are additional parents beyond the prospective three, nor whether this merge commit really is a stash: it just assumes that if it has the right set of parent-age, you intend to use it as one.
3This might be sensible enough for Git neophytes who are not trying to stash the index separately and used --index
on git stash apply
or git stash pop
without understand it. Once you do understand the index, though, it's clearly wrong: you wanted to restore the stashed index's changes relative to your current index, to your current index, not ignore them entirely! Committing your current index if appropriate, then committing your current work-tree if appropriate, and then turning the stash into a branch and committing its work-tree, gives you everything you need to build the correct final results.
4Technical details: the application uses git merge-recursive
—this is what implements git merge -s recursive
—with some secret environment variables to set the names on the conflict markers, if there is a conflict. The merge base is the commit that was HEAD
when the stash was made, the current tree is the the result of writing the current (at un-stash time) index, and the item being merged is the work-tree commit, or more precisely, its tree. This makes use of the fact that some merges can be run with uncommitted changes. The front end git merge
command prohibits merge attempts with uncommitted changes, as the results can be very messy when there's a problem.
1The fast-forward concept is also a bit more complicated than one typically sees it at first. That is, we see it when merging—see What is the difference between `git merge` and `git merge --no-ff`?—but it actually refers to updating a reference, such as a branch name. A branch name update is a fast-forward if and only if the new commit hash has the old commit hash as an ancestor, i.e., if git merge-base --is-ancester $old_hash $new_hash
returns a zero exit status.
When git merge
performs one of these fast-forward operations, it means that Git has changed the HEAD
commit to point to the new hash, and also updated the index and work-tree as necessary. If you were to fast-forward to the work-tree commit in the stash, that would expose the weird technically-a-merge work-tree commit to the rest of Git, where it would be, at the least, very confusing.
Note that git fetch
and git push
also perform fast-forward operations, or with --force
, allow non-fast-forward changes to branch and (for fetch) remote-tracking names. The receiver of a push normally requires a fast-forward because that means that the updated branch name contains all of the commits that it used to, plus some additional commits. A forced, non-fast-forward update discards commits from the branch (whether or not it adds new ones). The somewhat mysterious git fetch
output records whether a remote-tracking name was fast-forwarded or forced in three (!) ways:
$ git fetch
remote: Counting objects: 1701, done.
remote: Compressing objects: 100% (711/711), done.
remote: Total 1701 (delta 1363), reused 1318 (delta 989)
Receiving objects: 100% (1701/1701), 975.29 KiB | 3.65 MiB/s, done.
Resolving deltas: 100% (1363/1363), completed with 284 local objects.
From [url]
3e5524907..53f9a3e15 master -> origin/master
61856ae69..ad0ab374a next -> origin/next
+ fc16284ea...4bc8c995a pu -> origin/pu (forced update)
9125ddae1..9db014fc5 todo -> origin/todo
* [new tag] v2.18.0 -> v2.18.0
* [new tag] v2.18.0-rc2 -> v2.18.0-rc2
Note the +
in front of line recording the update to origin/pu
, and the words (forced updated)
added. That's two of the three ways. Pay attention to the dots between the two abbreviated commit hashes, though: all the other lines, which are not forced updates, show two dots, but this update shows three dots. That's because we can use git rev-list
or git log
with this same three-dot syntax to view the commits added and removed:
$ git log --oneline --decorate --graph --left-right fc16284ea...4bc8c995a
> 4bc8c995a (origin/pu) Merge branch 'sb/diff-color-move-more' into pu
|\
| > 76db2b132 SQUASH????? Documentation breakage emergency fix
| > f2d78d2c6 diff.c: add white space mode to move detection that allows indent changes
| > a58e68b88 diff.c: factor advance_or_nullify out of mark_color_as_move
[massive snippage]
< fc16284ea Merge branch 'mk/http-backend-content-length' into pu
|\
| < 202e4a2ff SQUASH???
| < cb6d3213e http-backend: respect CONTENT_LENGTH for receive-pack
< | 4486a82e5 Merge branch 'ag/rebase-p' into pu
< | a84cc85f3 Merge branch 'nd/completion-negation' into pu
[much more snippage]
The --left-right
option, along with the three-dot syntax, tells Git to mark which "side" the commits came from. In this case the >
commits are now on the pickup branch, and the <
commits have been taken off it. These particular removed commits are now entirely unreferenced and will be garbage collected soon(ish).
Git push not working in visual studio 2015
On further inspection I found a difference between the GIT command line client and the builtin cliënt in visual studio. The latter uses TLS1.2 for the https session, whereas the command line uses TLS 1.0.
As it happens out private GITLAB server is situated behind a reverse proxy that has problems with TLS1.2 connections resulting in the weird behaviour (fetch & pull OK, no push).
Retract accidental checkin
NB: THIS PROBABLY WON'T WORK ON CURRENT VERSIONS OF SUBVERSION AND IS A BAD IDEA - but I've left it here for information
NB: Normally when you have checked in by mistake, you should just revert the commit - see the other answers to this question. However, if you want to know how to actually undo the effects of the commit and change the repository to be how it was before, there's some explanation below:
This isn't what you normally want, but if you really want to remove the actual committed version from the repository, then you can do a nasty rollback on the repository as follows (this assumes that $REV
is set to the latest revision, which you are removing):
- Backup your repository first as these changes may break it (and read the assumptions below)
- Revert your local copy to the previous revision so it doesn't get confused (
svn revert -r $((REV-1))
) - In the repository, remove
db/revs/$REV
anddb/revprops/$REV
- In the repository, remove
db/current
and (for subversion 1.6 or greater)db/rep-cache.db
, and runsvnadmin recover .
- (Possibly) adjust the permissions on
db/rep-cache.db
to prevent attempt to write a readonly database errors
This all assumes:
- You're using a
fsfs
-based repository - Subversion release greater than
1.5.0
(otherwise you have to manually editdb/current
and change the revision number rather than runningsvnadmin recover .
) - No other subsequent revisions have been committed
- You have write access to the filesystem of the repository
- You aren't scared of somebody else trying to access it while you do the above
I've done it when a huge file was committed to a repository that I didn't want to stay in the history (and mirrors etc) forever; it's not in any way ideal or normal practice...
Related Topics
How to Close a Netcat Connection After a Certain Character Is Returned in the Response
How to Save Output of "Watch" to File
Should Linux Cron Jobs Be Specified with an "&" to Indicate to Run in Background
Why Is There No Directx API for Linux
Search for a Cronjob with Crontab -L
What Happens to Other Processes When a Docker Container's Pid1 Exits
Why Is My Bash Script Adding <Feff> to the Beginning of Files
How to Tell What a Linux Process Is Waiting For
Dummy Questions About Setting Up Git on Amazon Cloud Ec2
Hook Functions for Linux Filesystem
Using Opengl Without X-Window System
Deleting Old Files Using Crontab
Why Does Arm-Linux-Gnueabi-G++-4.4 Always Build a "7-A" Binary