development

Perforce 사용자를위한 Git

big-blog 2020. 9. 22. 20:52
반응형

Perforce 사용자를위한 Git


저는 수년 동안 Perforce를 사용해 왔습니다. 내 개인 코드에 git을 사용하는 것으로 전환하고 싶지만 내가 본 모든 git 자습서에서는 사용자가 완전한 소스 제어 n00b (엄청나게 지루하게 만듭니다)이거나 익숙하다고 가정합니다. svn (나는 아닙니다).

저는 p4를 알고 있으며 분산 소스 제어 시스템에 대한 아이디어도 이해합니다 (따라서 판매 홍보가 필요하지 않습니다. 감사합니다). 내가 원하는 것은 p4 명령에서 동등한 git 명령으로의 변환 테이블과 p4와 동등한 명령이없는 "ca n't live without"명령입니다.

모든 p4 사용자가 p4의 다른 하위 집합을 사용한다고 생각하기 때문에 내가 본 문서에서 즉시 명확하지 않은 git에서 할 수 있기를 원하는 p4에서 정기적으로 수행하는 작업 중 일부는 다음과 같습니다. :

  1. 단일 클라이언트에서 여러 보류중인 변경 목록을 만듭니다. ( p4 change)
  2. 보류중인 변경 목록을 편집합니다. (또한 p4 change)
  3. 보류중인 모든 변경 목록 ( p4 changes -s pending) 목록보기
  4. 내 클라이언트 ( p4 opened) 또는 보류중인 변경 목록 ( p4 describe) 에서 변경된 모든 파일 목록
  5. (나는 이것에 대한 래퍼 스크립트를 사용하는 용도에 보류중인 변경 목록의 DIFF를 참조 p4 diff하고 p4 describe)
  6. 주어진 파일에 대해 제출 된 변경 목록이 어떤 행에 영향을 미치는지 확인하십시오 ( p4 annotate).
  7. 주어진 파일에 대해 파일에 영향을 준 변경 목록의 설명 목록을 참조하십시오 ( p4 log).
  8. 보류중인 변경 목록 제출 ( p4 submit -c)
  9. 보류중인 변경 목록 중단 ( p4 revert)

이 중 많은 부분이 "변경 목록"을 중심으로 이루어집니다. "changelist"는 p4 용어입니다. git에 해당하는 용어는 무엇입니까?

브랜치는 p4가 changelist라고 부르는 대신 git 사용자가 사용하는 것 같습니다. 약간 혼란 스럽습니다. p4에는 모호하게 관련된 개념 인 것처럼 보이지만 분기라는 것이 있기 때문입니다. (나는 항상 p4의 브랜치 개념이 꽤 이상하다고 생각했지만 브랜치의 고전적인 RCS 개념과는 또 다릅니다.)

어쨌든 ... git의 브랜치로 p4 변경 목록에서 일반적으로 수행하는 작업을 수행하는 방법을 모르겠습니다. p4에서는 다음과 같이 할 수 있습니다.

$ p4 edit a.txt
$ p4 change a.txt
Change 12345 created.

이 시점에서 a.txt가 포함 된 변경 목록이 있습니다. 변경 목록을 제출하지 않고도 설명을 편집하고 계속 작업 할 수 있습니다. 또한 다른 코드 레이어의 버그 수정과 같이 다른 파일을 변경해야하는 경우 동일한 클라이언트에서 수행 할 수 있습니다.

$ p4 edit z.txt
$ p4 change z.txt
Change 12346 created.

이제 동일한 클라이언트에 두 개의 개별 변경 목록이 있습니다. 나는 이것들을 동시에 작업 할 수 있으며, 그들 사이를 "전환"하기 위해 아무것도 할 필요가 없습니다. 커밋 할 때가되면 별도로 제출할 수 있습니다.

$ p4 submit -c 12346  # this will submit the changes to z.txt
$ p4 submit -c 12345  # this will submit the changes to a.txt

나는 이것을 git에서 복제하는 방법을 알 수 없습니다. 내 실험 git add에서 현재 분기와 관련된 것으로 보이지 않습니다 . 내가 말할 수있는 한, 내가 어떤 지점에 있었는지에 관계없이 git commit내가 모든 파일을 커밋 git add할 때 :

$ git init
Initialized empty Git repository in /home/laurence/git-playground/.git/
$ ls
a.txt  w.txt  z.txt
$ git add -A .
$ git commit
 Initial commit.
 3 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
 create mode 100644 w.txt
 create mode 100644 z.txt
$ vi a.txt z.txt 
2 files to edit
$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   a.txt
#   modified:   z.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git branch aardvark
$ git checkout aardvark
M   a.txt
M   z.txt
Switched to branch 'aardvark'
$ git add a.txt 
$ git checkout master
M   a.txt
M   z.txt
Switched to branch 'master'
$ git branch zebra
$ git checkout zebra
M   a.txt
M   z.txt
Switched to branch 'zebra'
$ git add z.txt 
$ git status
# On branch zebra
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a.txt
#   modified:   z.txt
#
$ git checkout aardvark
M   a.txt
M   z.txt
Switched to branch 'aardvark'
$ git status
# On branch aardvark
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a.txt
#   modified:   z.txt

이 예에서 aardvark 및 zebra 분기는 정확히 동일한 변경 세트를 포함하는 것으로 보이며 그 결과에 git status따라 둘 중 하나에서 커밋을 수행하면 동일한 효과가 나타납니다. 내가 뭘 잘못하고 있니?


나는 perforce를 많이 사용하지 않았기 때문에 이것이 정확히 1 : 1 번역이 아닐 수도 있습니다. 그런 다음 git 및 mercurial과 같은 분산 소스 제어 시스템은 어쨌든 다른 워크 플로우를 가지므로 실제로 1 : 1 번역이 없습니다 (그리고 없어야합니다). 어쨌든, 여기에 간다 :

  • 보류중인 여러 변경 목록 만들기-> 대신 분기를 사용합니다. git 브랜치는 가볍고 빠르며 생성하는 데 1 초도 걸리지 않으며 일반적으로 병합하는 데 2 ​​초도 걸리지 않습니다. 자주 분기하고 리베이스하는 것을 두려워하지 마십시오.

    git branch new-branch-name
    git checkout new-branch-name
    

    또는 한 줄로 모두 수행하십시오.

    git checkout -b new-branch-name
    
  • See a list of all pending changelists -> Since the equivalent of multiple pending changelist is multiple branches just view the branches:

    git branch
    

    If you want to view remote branches as well:

    git branch -a
    

    It is considered good practice to immediately delete a branch after a successful merge so you don't have to keep track of which branch are pending to be merged and which have already been merged.

  • List all changed files -> For a single pending "changelist" in a specific branch git has a concept of the index or cache. In order to commit a change you must first add files to this index. This allows you to manually select which group of files represent a single change or to ignore irrelevant files. To see the status of which files are added, or not to this index just do:

    git status
    
  • See a diff of a pending changelist -> There are two parts to this. First to see a diff between the working directory and the index:

    git diff
    

    But if you want to know the diff between what you're typing now and the last commit then you are really asking for a diff between the working directory+index and the HEAD:

    git diff HEAD
    
  • For a given file, see which submitted changelists affected which lines -> This is easy:

    git blame filename
    

    or even better, if you are in a windowing environment:

    git gui blame filename
    

    Git gui takes longer to parse the file (it was written in tcl instead of C) but it has lots of neat features including the ability to "time travel" back into the past by clicking on a commit ID. I only wish they'd implement a feature to "time travel" to the future so I can find out how a given bug will finally be resolved ;-)

  • For a given file, see a list of the descriptions of the changelists that affected the file -> also easy:

    git log filename
    

    But git log is a much more powerful tool than just this. In fact most of my personal scripts piggyback off-of git log to read the repository. Read the man page.

  • Submit a pending changelist -> Also easy:

    git commit
    

See my answer to a previous question to see my typical git workflow: Learning Git. Need to know if I am on the right track

If you follow the workflow I outlined then you'll find tools like gitk to be much more valuable since it allows you to clearly see groups of changes.


Additional answer:

Git is very flexible and there are several ways to do what you describe. The thing to remember is to always start a new branch for each feature you're working on. This means the master branch isn't touched so you can always go back to it to do bug fixes. Working in git one should almost always start with:

git checkout -b new-feature-a

Now you can edit file a.txt. To work concurrently on another feature do:

git checkout master
git checkout -b new-feature-z

Now you can edit file z.txt. To switch back to a.txt:

git checkout new-feature-a

But wait, there are changes to new-feature-z and git won't let you switch branches. At this point you have two choices. The first is the simplest, commit all changes to the current branch:

git add .
git commit
git checkout new-feature-a

This is what I'd recommend. But if you are really not ready to commit the code, you can temporarily stash it:

git stash

Now you can switch to branch new-feature-a. To go back to the code you were working on just pop the stash:

git checkout new-feature-z
git stash pop

When all is done merge back all changes to master:

git merge --no-ff new-feature-a
git merge --no-ff new-feature-z

Because merges are so quick and easy (easy because conflicts are so rare and conflict resolution, when one does happen, not too hard) we use branches in git for everything.

Here's another example of a common use of branches in git that you don't see in other source control tools (except perhaps mercurial):

Need to keep changing your config files to reflect your dev environment? Then use a branch:

git checkout -b dev-config

Now edit your config files in your favourite editor then commit changes:

git add .
git commit

Now every new branch can start from the dev-config branch instead of master:

git checkout dev-config
git checkout -b new-feature-branch

Once you're done remove the edits in dev-config from new-feature-branch using interactive rebase:

git rebase -i master

Delete the commits you don't want then save. Now you have a clean branch without custom config edits. Time to merge back to master:

git checkout master
git merge --no-ff new-feature-branch
# because master have changed, it's a good idea to rebase dev-config:
git checkout dev-config
git rebase master

It should be noted that removing edits with git rebase -i even works when all changes happen in the same file. Git remembers changes, not file content*.

*note: actually, technically not entirely true but as a user that's what it feels like


More additional answer:

So, from you comments it looks like you want to have two branches to exist simultaneously so you can test how the combined code works. Well, this is a good way to illustrate the power and flexibility of branches.

First, a word on the implication of cheap branching and modifiable history on your workflow. When I was using CVS and SVN I was always a bit reluctant to commit. That's because committing unstable code would inevitably f**k up other people's working code. But with git I lost that fear. That's because in git other people won't get my changes until I merge them to master. So now I find myself committing code every 5 lines I write. You don't need perfect foresight to commit. You just need to change your mindset: commit-to-branch==add-to-changeset, merge-to-master==commit-changeset.

So, back to examples. Here's how I would do it. Say you have a branch new-feature-z and you want to test it with new-feature-a. I would just create a new branch to test it:

# assume we are currently in branch new-feature-z
# branch off this branch for testing
git checkout -b feature-z-and-feature-a
# now temporarily merge new-feature-a
git merge --no-ff new-feature-a

Now you can test. If you need to modify something to make feature-z work with feature-a then do so. If so you can merge back the changes to the relevant branch. Use git rebase -i to remove irrelevant changes from the merge.

Alternatively, you can also use git rebase to temporarily change the base of new-feature-z to point to new-feature-a:

# assume we are currently in branch new-feature-z
git rebase new-feature-a

Now the branch history is modified so that new-feature-z will be based off new-feature-a instead of master. Now you can test. Any changes committed in this branch will belong to the branch new-feature-z. If you need to modify new-feature-a just switch back to it and the rebase to get the new changes:

git checkout new-feature-a
# edit code, add, commit etc..
git checkout new-feature-z
git rebase new-feature-a
# now new-feature-z will contain new changes from new-feature-a

When you're done, simply rebase back to master to remove changes from new-feature-a:

# assume we are currently in branch new-feature-z
git rebase master

Don't be afraid to start a new branch. Don't be afraid to start a throwaway branch. Don't be afraid to throw away branches. And since merge==submit and commit==add-to-changeset don't be afraid to commit often. Remember, commit is a developer's ultimate undo tool.

Oh, and another thing, in git deleted branches still exist in your repository. So if you've accidentally deleted something that you later realise is useful after all you can always get it back by searching the history. So don't be afraid to throw away branches.


I don't have enough p4 experience to produce an actual cheat sheet, but there are at least some similarities to fall back on. A p4 "changeset" is a git "commit".

Changes to your local work space get added to the "index" with git add, and the index later gets committed with git commit. So the index is your pending changelist, for all intents and purposes.

You look at changes with git diff and git status, where git diff usually shows changes between the workspace and the index, but git diff --cached shows changes between the index and the repository (= your pending changelist).

For more in depth information, I recommend http://progit.org/book/. Since you know version control in general, you can probably skim a lot of it and extract the git-specific information...


I suffer like you with the lack of the "changelist" concept which is not exactly the same as git branches.

I would write a small script that will create a changelist file with the list of files in that changelist.

Another command to submit just a certain changelist by simply calling git commit -a @change_list_contents.txt and then "git commit"

Hope that helps, Elias


There is a more lightweight alternative in git that could form part of your workflow; using the git staging area.

I often just make changes then submit as several commits (e.g. add debug statements, refactor, actually fix a bug). Rather than setting up your perforce changelists, then make changes, then submit, you can just make your changes then choose how to submit them (optionally using the git staging area).

You can commit particular files from the command line with:

git commit a.txt
git commit z.txt

Or explicitly staging the files first:

git add a.txt
git commit
git add z.txt
git commit

git gui will let you select lines or hunks from within files to build up a commit in the staging area. This is very useful if you have changes in one file that you want to be in different commits. Having moved from git to perforce and this is one thing that I really miss.

There is a small caveat to bear in mind with this workflow. If you make changes A and B to a file, test the file, then commit A then you haven't tested that commit (independently of B).


This doesn't answer your question specifically, but I don't know if you are aware that a 2 User, 5 Workspace version of perforce is free to download and use from the perforce website.

This way you can use perforce at home for your personal projects if you wish. The one annoyance is the 5 workspaces which can be a bit limiting, but its pretty incredible to have perforce available for home use.


Having used both Perforce and git fairly extensively, there's only one way I can see to get anywhere near Perforce changelists with git.

The first thing to understand is that to correctly implement this functionality in git in such a way that it's a not a complete kluge, e.g. trying to shoehorn it into branches, would require the following change: git would require multiple staging areas for a single branch.

Perforce changelists permit a workflow that simply has no equivalent in git. Consider the following workflow:

Check out a branch
Modify file A and add it to changelist 1
Modify file B and add it to changelist 2

If you try to do this using branches in git you'll wind up with two branches, one of which has the changes to file A, the other has the changes to file B, but no place where you can see the changes to both files A and B at the same time.

The closest approximation I can see is to use git add . -p and then use the 'a' and 'd' sub-commands to select or reject entire files. However that's not quite the same, and the difference here stems from a fundamental disparity in the general modus operandi of the two systems.

Git (and subversion, not that it matters for this discussion) allow a file to be changed without telling anyone about this ahead of time. You just change a file, and then let git sort it all out when you commit the changes. Perforce requires you to actively check out a file before changes are allowed, and it is for this reason that changelists have to exist. In essence, Perforce requires you to add a file to the index before changing it. Hence the necessity for multiple changelists in Perforce, and also the reason why git has no equivalent. It simply doesn't need them.

참고URL : https://stackoverflow.com/questions/3656351/git-for-perforce-users

반응형