开发者

How to tell Git that it's the same directory, just a different name

After getting past a few hurdles learning Git, I came across a new challenge: Renaming a directory (locally, in the working directory).

When I type git status, it lists all the files in the old directory name (that exist with the same exact filenames in the new directory) as deleted and the new directory name as "untracked".

Is there a way to tell Git that "it's actually the same directory, just a different name"?

So that all the files will be listed by git status as modified only?

To exemplify the problem, he开发者_运维百科re is the output I receive from git status when I rename an entire directory:

git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    old-dir-name/file1
#   deleted:    old-dir-name/file2
#   deleted:    old-dir-name/file3
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   new-dir-name/
no changes added to commit (use "git add" and/or "git commit -a")
~/sb/ws>


Just git add the directory at its new name. Git doesn't explicitly track renames, it just detects them later. git mv might be marginally more efficient to perform (because it can update the index directly), but the effect is exactly the same.


All answers here were very helpful on the way to the actual solution for the particular scenario I was facing. I am providing what worked for me in the form of actual steps, hopefully helping others encountering the same challenge:

  1. git mv <old-dir-name> <new-dir-name>
  2. git status (verify that all files marked renamed, not "deleted")
  3. git commit -a -m "git mv <old-dir-name> <new-dir-name>" (this is a "fake" commit, to prepare for the real rename in the next steps)
  4. git branch git_mv_20110708_1500_DO_NOT_USE ("fake" branch with timestamp reminding that we only did this as a workaround)
  5. /bin/rm -Rf <new-dir-name>
  6. cp -Rp .../<new-dir-name> . (copy over the actual folder with the renamed name)
  7. git status (most modified files will now be marked correctly as modified, not "deleted". Files that have been renamed will be marked as deleted and added, despite having the same content! -- repeated steps 1-7 for those files if rename tracking is needed for them too)
  8. git add <untracked files>
  9. git commit -a -m "finally renamed this folder"
  10. git branch FOLDER_RENAMED :)

P.S. gitk loves this, but Emacs is still confused with the renames. :(


You need to use git's mv: http://www.kernel.org/pub/software/scm/git/docs/git-mv.html

git mv old_dir new_dir

So you'll need to move the new dir back to the old, and re-move it with mv.

EDIT: To answer the responses to my answer:

I decided to test this out myself. I created two files with distinct text, and used git mv on one, and mv file file2, git add file2, git add -u on the other. Commit message indicated that both were tracked as renames. Therefore, all my advice does is save a step, as others were saying.


Git 2.18 (Q2 2018) should to detect "that it's the same directory, just a different name", because "git status" learned to pay attention to UI related diff configuration variables such as diff.renames.

See commit dc6b1d9 (04 May 2018) by Eckhard S. Maaß (``).
(Merged by Junio C Hamano -- gitster -- in commit 1e174fd, 23 May 2018)

wt-status: use settings from git_diff_ui_config

If you do something like:

- git add .
- git status
- git commit
- git show (or git diff HEAD)

one would expect to have analogous output from git status and git show (or similar diff-related programs).
This is generally not the case, as git status has hard coded values for diff related options.

With this commit the hard coded settings are dropped from the status command in favour for values provided by git_diff_ui_config.

What follows are some remarks on the concrete options which were hard coded in git status:

`diffopt.detect_rename`

Since the very beginning of git status in a3e870f ("Add "commit" helper script", 2005-05-30, Git v0.99), git status always used rename detection, whereas with commands like show and log one had to activate it with a command line option.
After 5404c11 ("diff: activate diff.renames by default", 2016-02-25, Git v2.9.0) the default behaves the same by coincidence, but changing diff.renames to other values can break the consistency between git status and other commands again.
With this commit one control the same default behaviour with diff.renames.

`diffopt.rename_limit`

Similarly one has the option diff.renamelimit to adjust this limit for all commands but git status. With this commit git status will also honor those.


And the same Git 2.18 offer status.renames, which could be useful for those who want to do so without disabling the default rename detection done by the "git diff" command.

See commit e8b2dc2 (11 May 2018) by Ben Peart (benpeart).
(Merged by Junio C Hamano -- gitster -- in commit 5da4847, 30 May 2018)

add status config and command line options for rename detection

After performing a merge that has conflicts git status will, by default, attempt to detect renames which causes many objects to be examined.
In a virtualized repo, those objects do not exist locally so the rename logic triggers them to be fetched from the server. This results in the status call taking hours to complete on very large repos vs seconds with this patch.

Add a new config status.renames setting to enable turning off rename detection during status and commit.
This setting will default to the value of diff.renames.

Add a new config status.renamelimit setting to to enable bounding the time spent finding out inexact renames during status and commit.
This setting will default to the value of diff.renamelimit.

Add --no-renames command line option to status that enables overriding the config setting from the command line.
Add --find-renames[=<n>] command line option to status that enables detecting renames and optionally setting the similarity index.


The problem is a User interface / User experience problem, rather than a 'real' problem for git. I've asked the same question how-to-work-with-subdirectory-renames-in-git and an underlying question how-does-git-record-or-more-likely-represent-file-paths-and-names-for-its-blob

In fact Git doesn't actually care. All the messages you (we) get are about what 'diff' thinks might have happened. With other options the messages will be different but the repository won't be any different (it's the same snapshot!).

One option style is the subtree option for diff (though it is for a different purpose), as is --patience.

There is a need for a better UI/UX option that would more easily detect pathchanges (based on the trees rather than the blobs). Part of the problem is the subtle mistake in Linus's argument. He is right that the Git repo should not store the rename (pathchange) explicitly, rather we need an option to allow it's proper discovery.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜